<?php
/**
 * @version		$Id:plugin.php 6961 2007-03-15 16:06:53Z tcp $
 * @package		Joomla.Framework
 * @subpackage	Installer
 * @copyright	Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
 * @license		GNU/GPL, see LICENSE.php
 * Joomla! is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 * See COPYRIGHT.php for copyright notices and details.
 */
 
 /**
 * Modified for use as the JCE plugin and language installer
 * Ryan Demmer 2007-08-27
 */

// Check to ensure this file is within the rest of the framework
defined('JPATH_BASE') or die();

define( 'JCE_PATH', JPATH_PLUGINS.DS.'editors'.DS.'jce' );
define( 'JCE_PLUGINS', JCE_PATH.DS.'tiny_mce'.DS.'plugins' );

require_once( JPATH_COMPONENT .DS. 'plugins' .DS. 'plugin.php' );

/**
 * Plugin installer
 *
 * @package		Joomla.Framework
 * @subpackage	Installer
 * @since		1.5
 * Renamed JInstallerPlugin to JCEInstallerPlugin
 */
class JCEInstallerPlugin extends JObject
{
	/**
	 * Constructor
	 *
	 * @access	protected
	 * @param	object	$parent	Parent object [JInstaller instance]
	 * @return	void
	 * @since	1.5
	 */
	function __construct(&$parent)
	{
		$this->parent =& $parent;
	}

	/**
	 * Custom install method
	 *
	 * @access	public
	 * @return	boolean	True on success
	 * @since	1.5
	 * Minor alteration - see below
	 */
	function install()
	{
		// Get a database connector object
		$db =& $this->parent->getDBO();

		// Get the extension manifest object
		$manifest =& $this->parent->getManifest();
		$this->manifest =& $manifest->document;

		/**
		 * ---------------------------------------------------------------------------------------------
		 * Manifest Document Setup Section
		 * ---------------------------------------------------------------------------------------------
		 */

		// Set the component name
		$name =& $this->manifest->getElementByPath('name');
		$this->set('name', $name->data());
		
		// Get the component description
		$description = & $this->manifest->getElementByPath('description');
		if (is_a($description, 'JSimpleXMLElement')) {
			$this->parent->set('message', $description->data());
		} else {
			$this->parent->set('message', '' );
		}
		
		$element =& $this->manifest->getElementByPath('files');

		// Plugin name is specified
		$pname 	= $this->manifest->attributes('plugin');

		if (!empty ($pname)) {
			// ^ Use JCE_PLUGINS defined path
			$this->parent->setPath('extension_root', JCE_PLUGINS .DS. $pname);
		} else {
			$this->parent->abort('Extension Install: '.JText::_('No JCE plugin file specified'));
			return false;
		}

		/**
		 * ---------------------------------------------------------------------------------------------
		 * Filesystem Processing Section
		 * ---------------------------------------------------------------------------------------------
		 */

		// If the extension directory does not exist, lets create it
		$created = false;
		if (!file_exists($this->parent->getPath('extension_root'))) {
			if (!$created = JFolder::create($this->parent->getPath('extension_root'))) {
				$this->parent->abort('Plugin Install: '.JText::_('Failed to create directory').': "'.$this->parent->getPath('extension_root').'"');
				return false;
			}
		}

		/*
		 * If we created the extension directory and will want to remove it if we
		 * have to roll back the installation, lets add it to the installation
		 * step stack
		 */
		if ($created) {
			$this->parent->pushStep(array ('type' => 'folder', 'path' => $this->parent->getPath('extension_root')));
		}

		// Copy all necessary files
		if ($this->parent->parseFiles($element, -1) === false) {
			// Install failed, roll back changes
			$this->parent->abort();
			return false;
		}

		// Parse optional tags -- language files for plugins
		$this->parent->parseLanguages($this->manifest->getElementByPath('languages'), 0);
		
		// If there is an install file, lets copy it.
		$installScriptElement =& $this->manifest->getElementByPath('installfile');
		if (is_a($installScriptElement, 'JSimpleXMLElement')) {
			// Make sure it hasn't already been copied (this would be an error in the xml install file)
			if (!file_exists($this->parent->getPath('extension_root').DS.$installScriptElement->data()))
			{
				$path['src']	= $this->parent->getPath('source').DS.$installScriptElement->data();
				$path['dest']	= $this->parent->getPath('extension_root').DS.$installScriptElement->data();
				if (!$this->parent->copyFiles(array ($path))) {
					// Install failed, rollback changes
					$this->parent->abort(JText::_('Component').' '.JText::_('Install').': '.JText::_('Could not copy PHP install file.'));
					return false;
				}
			}
			$this->set('install.script', $installScriptElement->data());
		}

		// If there is an uninstall file, lets copy it.
		$uninstallScriptElement =& $this->manifest->getElementByPath('uninstallfile');
		if (is_a($uninstallScriptElement, 'JSimpleXMLElement')) {
			// Make sure it hasn't already been copied (this would be an error in the xml install file)
			if (!file_exists($this->parent->getPath('extension_root').DS.$uninstallScriptElement->data()))
			{
				$path['src']	= $this->parent->getPath('source').DS.$uninstallScriptElement->data();
				$path['dest']	= $this->parent->getPath('extension_root').DS.$uninstallScriptElement->data();
				if (!$this->parent->copyFiles(array ($path))) {
					// Install failed, rollback changes
					$this->parent->abort(JText::_('Component').' '.JText::_('Install').': '.JText::_('Could not copy PHP uninstall file.'));
					return false;
				}
			}
		}

		/**
		 * ---------------------------------------------------------------------------------------------
		 * Database Processing Section
		 * ---------------------------------------------------------------------------------------------
		 */

		// Check to see if a plugin by the same name is already installed
		// ^ Altered db query for #__jce_plugins
		$query = 'SELECT `id`' .
				' FROM `#__jce_plugins`' .
				' WHERE name = '.$db->Quote($pname);
		$db->setQuery($query);
		if (!$db->Query()) {
			// Install failed, roll back changes
			$this->parent->abort('Plugin Install: '.$db->stderr(true));
			return false;
		}
		$id = $db->loadResult();

		// Was there a module already installed with the same name?
		if ($id) {
			
			if (!$this->parent->getOverwrite())
			{
				// Install failed, roll back changes
				$this->parent->abort('Plugin Install: '.JText::_('Plugin').' "'.$pname.'" '.JText::_('already exists!'));
				return false;
			}
			
		} else {
			$icon 		= $this->manifest->getElementByPath('icon');
			$layout 	= $this->manifest->getElementByPath('layout');
			$elements 	= $this->manifest->getElementByPath('elements');			
			// ^ Changes to plugin parameters. Use JCEPluginsTable class. 
			$row =& JTable::getInstance('plugin', 'JCETable');
			$row->title 		= $this->get('name');
			$row->name			= $this->manifest->attributes('plugin');
			$row->type 			= 'plugin';
			$row->row	 		= 4;
			$row->ordering 		= 1;
			$row->published 	= 1;
			$row->editable 		= 1;
			//$row->elements 		= $elements->data();
			$row->icon 			= $icon->data();
			$row->layout	 	= $layout->data();
			$row->iscore 		= 0;
			//$row->params 		= $this->parent->getParams();
			//$row->variables 	= $this->parent->getVariables();

			if (!$row->store()) {
				// Install failed, roll back changes
				$this->parent->abort('Plugin Install: '.$db->stderr(true));
				return false;
			}
						
			// Process default extension installation (files are assumed to have been copied!)
			$element =& $this->manifest->getElementByPath('extensions');
			if (is_a($element, 'JSimpleXMLElement') && count($element->children())) {
				$extensions =& $element->children();			
				foreach( $extensions as $extension ){										
					if( $extension->attributes('name') ){
						$query = 'INSERT INTO `#__jce_extensions` ' .
						' VALUES ("", '.(int) $row->id.', '.$db->Quote( $extension->attributes('title') ).', '.$db->Quote( $extension->attributes('name') ).', '.$db->Quote( $extension->attributes('folder') ).', 1 )';
						
						$db->setQuery($query);
						if (!$db->query()) {
							// Install failed, raise error
							JError::raiseWarning(100, 'Plugin Install: Unable to install default extension '. $extension->attributes('title'));
							return false;
						}
					}
				}
			}			
			
			// Since we have created a plugin item, we add it to the installation step stack
			// so that if we have to rollback the changes we can undo it.
			$this->parent->pushStep(array ('type' => 'plugin', 'id' => $row->id));
		}
		
		/**
		 * ---------------------------------------------------------------------------------------------
		 * Custom Installation Script Section
		 * ---------------------------------------------------------------------------------------------
		 */

		/*
		 * If we have an install script, lets include it, execute the custom
		 * install method, and append the return value from the custom install
		 * method to the installation message.
		 */
		if ($this->get('install.script')) {
			if (is_file($this->parent->getPath('extension_root').DS.$this->get('install.script'))) {
				ob_start();
				ob_implicit_flush(false);
				require_once ($this->parent->getPath('extension_root').DS.$this->get('install.script'));
				if (function_exists('com_install')) {
					if (com_install() === false) {
						$this->parent->abort(JText::_('Plugin').' '.JText::_('Install').': '.JText::_('Custom install routine failure'));
						return false;
					}
				}
				$msg = ob_get_contents();
				ob_end_clean();
				if ($msg != '') {
					$this->parent->set('extension.message', $msg);
				}
			}
		}
		
		/**
		 * ---------------------------------------------------------------------------------------------
		 * Install plugin into Default Group
		 * ---------------------------------------------------------------------------------------------
		 */
		 // Add to Default Group
			if( $row->type == 'plugin' ){				
				JTable::addIncludePath( JPATH_ADMINISTRATOR .DS. 'components' .DS. 'com_jce' .DS. 'groups' );
				$grow =& JTable::getInstance('groups', 'JCETable');
				
				$query = 'SELECT id'
				. ' FROM #__jce_groups'
				. ' WHERE name = '. $db->Quote('Default')
				;
				$db->setQuery( $query );
				$gid = $db->loadResult();
				
				$grow->load( $gid );				
				// Add to plugins list
				$grow->plugins 	= $grow->plugins . ',' . $row->id;
				// Add to last row if plugin has a layout icon
				if( $row->layout ){
					$grow->rows		= $grow->rows . ',' . $row->id;
				}				
				if (!$grow->store()) {
					JError::raiseWarning(100, 'Plugin Install: Unable to add plugin to Default group' );
				}
			 }

		/**
		 * ---------------------------------------------------------------------------------------------
		 * Finalization and Cleanup Section
		 * ---------------------------------------------------------------------------------------------
		 */

		// Lastly, we will copy the manifest file to its appropriate place.
		if (!$this->parent->copyManifest(-1)) {
			// Install failed, rollback changes
			$this->parent->abort('Plugin Install: '.JText::_('Could not copy setup file'));
			return false;
		}
		
		return true;
	}

	/**
	 * Custom uninstall method
	 *
	 * @access	public
	 * @param	int		$cid	The id of the plugin to uninstall
	 * @param	int		$clientId	The id of the client (unused)
	 * @return	boolean	True on success
	 * @since	1.5
	 */
	function uninstall($id, $clientId )
	{
		// Initialize variables
		$row	= null;
		$retval = true;
		$db		=& $this->parent->getDBO();

		// First order of business will be to load the module object table from the database.
		// This should give us the necessary information to proceed.

		// ^ Changes to plugin parameters. Use JCEPluginsTable class. 
		$row =& JTable::getInstance('plugin', 'JCETable');
		$row->load((int) $id);

		// Is the plugin we are trying to uninstall a core one?
		// Because that is not a good idea...
		if ($row->iscore) {
			JError::raiseWarning(100, 'Plugin Uninstall: '.JText::sprintf('WARNCOREPLUGIN', $row->title)."<br />".JText::_('WARNCOREPLUGIN2'));
			return false;
		}

		// Get the plugin folder so we can properly build the plugin path
		if (trim($row->name) == '') {
			JError::raiseWarning(100, 'Plugin Uninstall: '.JText::_('Plugin field empty, cannot remove files'));
			return false;
		}

		// Set the plugin root path
		$this->parent->setPath('extension_root', JCE_PLUGINS . DS . $row->name);

		$manifestFile = $this->parent->getPath('extension_root') . DS . $row->name . '.xml';

		if (file_exists($manifestFile))
		{
			$xml =& JFactory::getXMLParser('Simple');

			// If we cannot load the xml file return null
			if (!$xml->loadFile($manifestFile)) {
				JError::raiseWarning(100, 'Plugin Uninstall: '.JText::_('Could not load manifest file'));
				return false;
			}

			/*
			 * Check for a valid XML root tag.
			 * @todo: Remove backwards compatability in a future version
			 * Should be 'install', but for backward compatability we will accept 'mosinstall'.
			 */
			$root =& $xml->document;
			if ($root->name() != 'install' && $root->name() != 'mosinstall') {
				JError::raiseWarning(100, 'Plugin Uninstall: '.JText::_('Invalid manifest file'));
				return false;
			}

			// Remove the plugin files
			$this->parent->removeFiles($root->getElementByPath('files'), -1);
			JFile::delete($manifestFile);

			// Remove all media and languages as well
			$this->parent->removeFiles($root->getElementByPath('languages'), 0);
			
			/**
			 * ---------------------------------------------------------------------------------------------
			 * Custom Uninstallation Script Section
			 * ---------------------------------------------------------------------------------------------
			 */
	
			// Now lets load the uninstall file if there is one and execute the uninstall function if it exists.
			$uninstallfileElement =& $root->getElementByPath('uninstallfile');
			if (is_a($uninstallfileElement, 'JSimpleXMLElement')) {
				// Element exists, does the file exist?
				if (is_file($this->parent->getPath('extension_root').DS.$uninstallfileElement->data())) {
					ob_start();
					ob_implicit_flush(false);
					require_once ($this->parent->getPath('extension_root').DS.$uninstallfileElement->data());
					if (function_exists('com_uninstall')) {
						if (com_uninstall() === false) {
							JError::raiseWarning(100, JText::_('Plugin').' '.JText::_('Uninstall').': '.JText::_('Custom Uninstall script unsuccessful'));
							$retval = false;
						}
					}
					$msg = ob_get_contents();
					ob_end_clean();
					if ($msg != '') {
						$this->parent->set('extension.message', $msg);
					}
				}
			}
			
			// Remove extension installations from #__jce_extensions
			$query = 'DELETE' .
			' FROM `#__jce_extensions`' .
			' WHERE pid='.(int)$id;
			$db->setQuery($query);
			if( !$db->query() ){
				JError::raiseWarning(100, 'Plugin Uninstall: Unable to remove extension records.');
			}
			
			// Remove from Groups
			if( $row->type == 'plugin' ){				
				JTable::addIncludePath( JPATH_ADMINISTRATOR .DS. 'components' .DS. 'com_jce' .DS. 'groups' );
				$grow =& JTable::getInstance('groups', 'JCETable');
				
				$query = 'SELECT id, name, plugins, rows'
				. ' FROM #__jce_groups'
				;
				$db->setQuery( $query );
				$groups = $db->loadObjectList();
				
				foreach( $groups as $group ){
					$plugins = explode( ',', $group->plugins );
					// Existence check
					if( in_array( $row->id, $plugins ) ){
						// Load tables
						$grow->load( $group->id );
						// Remove from plugins list
						foreach( $plugins as $k => $v ){
							if( $row->id == $v ){
								unset( $plugins[$k] );
							}
						}
						$grow->plugins = implode( ',', $plugins );
						// Remove from rows
						if( $row->layout ){
							$lists = array();
							foreach( explode( ';', $group->rows ) as $list ){
								$icons = explode( ',', $list );
								foreach( $icons as $k => $v ){
									if( $row->id == $v ){
										unset( $icons[$k] );
									}
								}
								$lists[] = implode( ',', $icons );
							}
							$grow->rows = implode( ';', $lists );
						}						
						if (!$grow->store()) {
							JError::raiseWarning(100, 'Plugin Install: Unable to remove plugin from Group: '. $grow->name );
						}
					}
				}
			 }
			
			// Now we will no longer need the plugin object, so lets delete it
			$row->delete($row->id);
			unset ($row);
						
		} else {
			JError::raiseWarning(100, 'Plugin Uninstall: Manifest File invalid or not found. Plugin entry removed from database.');
			
			$row->delete($row->id);
			unset ($row);
			$retval = false;
		}
		// If the folder is empty, let's delete it
		$files = JFolder::files($this->parent->getPath('extension_root'));
		if (!count($files)) {
			JFolder::delete($this->parent->getPath('extension_root'));
		}

		return $retval;
	}

	/**
	 * Custom rollback method
	 * 	- Roll back the plugin item
	 *
	 * @access	public
	 * @param	array	$arg	Installation step to rollback
	 * @return	boolean	True on success
	 * @since	1.5
	 * Minor changes to the db query
	 */
	function _rollback_plugin($arg)
	{
		// Get database connector object
		$db =& $this->parent->getDBO();

		// Remove the entry from the #__jce_plugins table
		$query = 'DELETE' .
				' FROM `#__jce_plugins`' .
				' WHERE id='.(int)$arg['id'];
		$db->setQuery($query);
		return ($db->query() !== false);
	}
}